package com.func.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson.JSON;
import com.func.domain.award.model.aggregate.GiveOutPrizesAggregate;
import com.func.domain.award.model.aggregate.UserAwardRecordAggregate;
import com.func.domain.award.model.entity.DistributeAwardEntity;
import com.func.domain.award.model.entity.TaskEntity;
import com.func.domain.award.model.entity.UserAwardRecordEntity;
import com.func.domain.award.model.entity.UserCreditAwardEntity;
import com.func.domain.award.model.valobj.AccountStatusVO;
import com.func.domain.award.repository.IAwardRepository;
import com.func.infrastructure.event.EventPublisher;
import com.func.infrastructure.persistent.dao.activity.IUserRaffleOrderDao;
import com.func.infrastructure.persistent.dao.award.IUserAwardRecordDao;
import com.func.infrastructure.persistent.dao.credit.IUserCreditAccountDao;
import com.func.infrastructure.persistent.dao.strategy.IAwardDao;
import com.func.infrastructure.persistent.dao.task.ITaskDao;
import com.func.infrastructure.persistent.po.activity.UserRaffleOrder;
import com.func.infrastructure.persistent.po.award.UserAwardRecord;
import com.func.infrastructure.persistent.po.credit.UserCreditAccount;
import com.func.infrastructure.persistent.po.task.Task;
import com.func.types.enums.ResponseCode;
import com.func.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

/**
 * <p>
 * AwardRecordRepository
 * </p>
 *
 * @author Simon
 * @version 2025.03.15.1.0.0
 * @description
 * @since 2025-03-15
 */

@Repository
@Slf4j
public class AwardRepository implements IAwardRepository {

    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private ITaskDao taskDao;
    @Resource
    private IUserAwardRecordDao userAwardRecordDao;
    @Resource
    private IAwardDao awardDao;
    @Resource
    private IUserCreditAccountDao creditAccountDao;
    @Resource
    private IUserRaffleOrderDao userRaffleOrderDao;
    @Override
    public void saveAwardRecord(UserAwardRecordAggregate userAwardRecordAggregate) {
        // 将实体对象转化为持久化对象, 以便于后面进行数据库中的操作和DAO层交互
        UserAwardRecordEntity userAwardRecordEntity = userAwardRecordAggregate.getUserAwardRecordEntity();
        TaskEntity taskEntity = userAwardRecordAggregate.getTaskEntity();
        String userId = taskEntity.getUserId();
        Long activityId = userAwardRecordEntity.getActivityId();
        Integer awardId = userAwardRecordEntity.getAwardId();

        UserAwardRecord userAwardRecord  = new UserAwardRecord();
        userAwardRecord.setUserId(userAwardRecordEntity.getUserId());
        userAwardRecord.setActivityId(userAwardRecordEntity.getActivityId());
        userAwardRecord.setStrategyId(userAwardRecordEntity.getStrategyId());
        userAwardRecord.setOrderId(userAwardRecordEntity.getOrderId());
        userAwardRecord.setAwardId(userAwardRecordEntity.getAwardId());
        userAwardRecord.setAwardTitle(userAwardRecordEntity.getAwardTitle());
        userAwardRecord.setAwardTime(userAwardRecordEntity.getAwardTime());
        userAwardRecord.setAwardState(userAwardRecordEntity.getAwardState().getCode());

        // 抽奖订单持久化对象, 用于将订单标记为已经使用了, 也就是抽奖订单已经被消费了
        UserRaffleOrder userRaffleOrder = new UserRaffleOrder();
        userRaffleOrder.setUserId(userId);
        userRaffleOrder.setActivityId(activityId);
        userRaffleOrder.setOrderId(userAwardRecordEntity.getOrderId());

        Task task = new Task();
        task.setUserId(taskEntity.getUserId());
        task.setTopic(taskEntity.getTopic());
        task.setMessageId(taskEntity.getMessageId());
        task.setMessage(JSON.toJSONString(taskEntity.getMessage()));
        task.setState(taskEntity.getState().getCode());

        // 聚合对象入库
        try {
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    // 入库中奖记录
                    userAwardRecordDao.insert(userAwardRecord);

                    // 消费抽奖订单
                    int count = userRaffleOrderDao.updateUserRaffleOrderCompleted(userRaffleOrder);
                    if (count == 1) {
                        log.info("用户抽奖订单消费成功 userRaffleOrder : {}", userRaffleOrder);
                    }

                    // 入库插入到任务表
                    taskDao.insert(task);
                    return 1;

                } catch (DuplicateKeyException e) {
                    log.error("写入中奖记录，唯一索引冲突 userId: {} activityId: {} awardId: {}", userId, activityId, awardId, e);
                     status.setRollbackOnly();
                     throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
        }

        // MQ发送消息
        try {
            // 发送消息 [在事务外执行, 如果失败还有任务补偿]
            eventPublisher.publish(task.getTopic(), task.getMessage());
            // 更新数据库记录, task 任务表
            taskDao.updateTaskSendMessageCompleted(task);
        } catch (Exception e) {
            log.error("写入中奖记录, 发送MQ消息失败 userId: {} topic: {}", userId, taskEntity.getTopic());
            taskDao.updateTaskSendMessageFail(task);
        }
    }

    @Override
    public String queryAwardConfig(Integer awardId) {
        return awardDao.queryAwardConfig(awardId);
    }

    @Override
    public String queryAwardKey(Integer awardId) {
        return awardDao.queryAwardKey(awardId);
    }

    @Override
    public void saveGiveOutPrizesAggregate(GiveOutPrizesAggregate giveOutPrizesAggregate) {
        String userId = giveOutPrizesAggregate.getUserId();
        // 转化对象为持久化对象以执行事务
        UserCreditAwardEntity userCreditAwardEntity = giveOutPrizesAggregate.getUserCreditAwardEntity();
        UserAwardRecordEntity userAwardRecordEntity = giveOutPrizesAggregate.getUserAwardRecordEntity();

        // 发奖记录持久化对象
        UserAwardRecord userAwardRecordReq = new UserAwardRecord();
        userAwardRecordReq.setUserId(userId);
        userAwardRecordReq.setOrderId(userAwardRecordEntity.getOrderId());
        userAwardRecordReq.setAwardId(userAwardRecordEntity.getAwardId());

        // 用户积分账户持久化对象
        UserCreditAccount userCreditAccountReq = new UserCreditAccount();
        userCreditAccountReq.setUserId(userId);
        userCreditAccountReq.setTotalAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccountReq.setAvailableAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccountReq.setAccountStatus(AccountStatusVO.open.getCode());

        try {
            dbRouter.doRouter(giveOutPrizesAggregate.getUserId());
            transactionTemplate.execute(status -> {
                try {

                    // 更新积分 || 创建积分账户
                    UserCreditAccount userCreditAccountRes = creditAccountDao.queryUserCreditAccount(userCreditAccountReq);
                    if (null == userCreditAccountRes) {
                        creditAccountDao.insert(userCreditAccountReq);
                    } else {
                        creditAccountDao.updateAddAmount(userCreditAccountReq);
                    }

                    // 更新发奖记录
                    int updateAwardCount = userAwardRecordDao.updateAwardRecordCompletedState(userAwardRecordReq);
                    if (updateAwardCount == 0) {
                        log.warn("更新中奖记录, 重复更新拦截 userId: {}, giveOutPrizesAggregate: {}", userId, JSON.toJSON(giveOutPrizesAggregate));
                        status.setRollbackOnly();
                    }
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("更新中奖记录, 唯一索引冲入 userId: {}", userId);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
        }
    }

    @Override
    public void justConsumeAwardRecord(DistributeAwardEntity distributeAwardEntity) {
        try {
            dbRouter.doRouter(distributeAwardEntity.getUserId());
            UserAwardRecord userAwardRecordReq = new UserAwardRecord();
            userAwardRecordReq.setUserId(distributeAwardEntity.getUserId());
            userAwardRecordReq.setOrderId(distributeAwardEntity.getOrderId());
            userAwardRecordReq.setAwardId(distributeAwardEntity.getAwardId());

            userAwardRecordDao.updateAwardRecordCompletedState(userAwardRecordReq);
        } finally {
            dbRouter.clear();
        }
    }
}
